// 类似的参考文献：https://juejin.cn/post/6879739215869935624
import {CommitOptions,DispatchOptions,Store as VuexStore} from 'vuex'
// 引入挂载在store文件夹中index文件上的所有modules以及所有模块的类型声明
import {modules,RootState} from './index'

/**
 * 这个文件是Vuex4+TypeScript实现类型推测的文件
 * 文件需要在main.ts中引入
 * 然后在具体的文件中使用store的时候，就不能从vuex中引入
 * 需要从自己写的store中引入
 */

//获取modules的类型
type Modules = typeof modules
//获取所有模块下的mutations
// GetMutation是一个泛型，传进来一个T，这个T限制它是mutations 使用infer推断出它的类型为G,否则是never，或者unkonwn
type GetMutation<T> = T extends {mutations:infer G} ? G :never;
// 注意这两个不是同一个类型，名称都不一样
// 这里需要限制它的key值 必须在我们传入的参数中
type GetMutations<T> = {
    [K in keyof T]:GetMutation<T[K]>
}
type mutationsObj = GetMutations<Modules>

//获取所有模块下的actions
type GetAction<T> = T extends {actions:infer G} ? G :never;
type GetActions<T> = {
    [K in keyof T]:GetAction<T[K]>
}
type actionsObj = GetActions<Modules>

//获取所有模块下的getters
type GetGetter<T> = T extends {getters:infer G} ? G :never;
type GetGetters<T> = {
    [K in keyof T]:GetGetter<T[K]>
}
type gettersObj = GetGetters<Modules>

// 获取到所有模块之后，需要进行组装
// 类似这种效果 tabs/addTabe  menu/setCount
// 设置为泛型，传入两个参数，需要限制类型为string类型
type AddPrefix<prefix,keys> =`${prefix & string}/${keys & string}`
type Getkey<T,K> = AddPrefix<K,keyof T>;
type Getkeys<T> = {
    [K in keyof T]:Getkey<T[K],K>
}[keyof T]

type ss = Getkeys<mutationsObj>

//获取当前模块下每个函数的返回值
type GetFunc<T,A,B> = T[A & keyof T][B & keyof T[A & keyof T]];
type GetMethod<T> = {
    [K in Getkeys<T>]:K extends `${infer A}/${infer B}` ? GetFunc<T,A,B> : unknown
}

type GetMutationsFunc = GetMethod<mutationsObj>;
type GetActionsFunc = GetMethod<actionsObj>;
type GetGettersFunc = GetMethod<gettersObj>;

// Omit<K,T> 从指定类型中删除指定的属性，反回一个新的类型
// K是对象类型名称 T是剔除K类型中的属性名称
// 从vuex原来的属性中去掉'commit'|'getters'|'dispatch'，然后使用新的名称
// 通过omit从vuex中移除当前的属性，然后使用自定义的
export type CommonStore = Omit<VuexStore<RootState>,'commit' | 'getters' |'dispatch'> 
&
{
    commit<K extends keyof GetMutationsFunc,P extends Parameters<GetMutationsFunc[K]>[1]>(
        key:K,
        payload?:P,
        options?:CommitOptions
    ):ReturnType<GetMutationsFunc[K]>
}
&
{
    getters:{
        [K in keyof GetGettersFunc]:ReturnType<GetGettersFunc[K]>
    }
}
&
{
    dispatch<K extends keyof GetActionsFunc>(
        key:K,
        payload?:Parameters<GetActionsFunc[K]>[1],
        options?:DispatchOptions
    ):ReturnType<GetActionsFunc[K]>
}
